<template>
  <div id="cesiumContainer">
    <div id="toolbar">
      <table>
        <tbody>
          <tr>
            <td>缩放比例</td>
            <td>
              <input
                type="range"
                min="0"
                max="10"
                step="0.1"
                data-bind="value: scale, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: scale" />
            </td>
          </tr>
          <tr>
            <td>高度</td>
            <td>
              <input
                type="range"
                min="-100"
                max="100"
                step="1"
                data-bind="value: height, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: height" />
            </td>
          </tr>
          <tr>
            <td>绕X轴旋转</td>
            <td>
              <input
                type="range"
                min="-180"
                max="180"
                data-bind="value: rx, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: rx" />
            </td>
          </tr>
          <tr>
            <td>绕Y轴旋转</td>
            <td>
              <input
                type="range"
                min="-180"
                max="180"
                data-bind="value: ry, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: ry" />
            </td>
          </tr>
          <tr>
            <td>绕z轴旋转</td>
            <td>
              <input
                type="range"
                min="-180"
                max="180"
                data-bind="value: rz, valueUpdate: 'input'"
              />
              <input type="text" size="5" data-bind="value: rz" />
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>
<script setup>
/**
 * 本案例是展示 knockout 的使用与gltf文件的加载调整
 * knockout：mvvm 的框架，cesium 集成了一些操作 类似 threejs GUI
 */
import * as Cesium from "cesium";
import TOKEN from "@/utils/cesium/token.js";

onMounted(() => {
  Cesium.Ion.defaultAccessToken = TOKEN;
  // 实例化
  const viewer = new Cesium.Viewer("cesiumContainer", {
    // 界面控件 全部关闭
    animation: false, // 左下角的动画控制器组件
    baseLayerPicker: false, // 底图组件，选择三维数字地球的底图（imagery and terrain）。
    fullscreenButton: true, // 全屏组件
    vrButton: false, // VR模式
    geocoder: false, // 地理编码（搜索）组件
    homeButton: false, // 首页，点击之后将视图跳转到默认视角
    infoBox: false, // 信息框
    sceneModePicker: false, // 场景模式，切换2D、3D 和 Columbus View (CV) 模式。
    selectionIndicator: false, //是否显示选取指示器组件
    timeline: false, // 时间轴
    navigationHelpButton: false, // 帮助提示，如何操作数字地球。
    // 如果最初应该看到导航说明，则为true；如果直到用户明确单击该按钮，则该提示不显示，否则为false。
    navigationInstructionsInitiallyVisible: false,
  });
  // 隐藏logo
  viewer._cesiumWidget._creditContainer.style.display = "none";
  const gltfEntity = loadGltfModel(viewer);
  viewer.zoomTo(gltfEntity);

  console.log("gltfEntity", gltfEntity);

  // 使用 knockout 框架
  useKnockout(viewer, gltfEntity);
});

/**
 * 使用 knockout MVVM框架
 */
function useKnockout(viewer, gltfEntity) {
  const model = gltfEntity.model;
  // 数据对象
  const viewModel = {
    scale: 1.0, // 缩放比例
    longitude: -75.61209430782448, // 经度
    latitude: 40.042530623591624, // 纬度
    height: 0, // 高度
    rx: 0, // 绕x轴旋转
    ry: 0, // 绕y轴旋转
    rz: 0, // 绕z轴旋转
  };
  Cesium.knockout.track(viewModel); // 使用 Knockout 使 ViewModel 具有响应式
  const toolbar = document.getElementById("toolbar");
  Cesium.knockout.applyBindings(viewModel, toolbar); // 关联UI

  // 缩放比
  Cesium.knockout.getObservable(viewModel, "scale").subscribe(function (scale) {
    scale = Number(scale);
    if (isNaN(scale)) return;
    model.scale = Number(viewModel.scale);
  });

  // 调整高度
  Cesium.knockout
    .getObservable(viewModel, "height")
    .subscribe(function (height) {
      height = Number(height);
      if (isNaN(height)) return;
      // 获取当前位置笛卡尔坐标
      const cartesian3 = gltfEntity.position.getValue(viewer.clock.currentTime);
      // 笛卡尔坐标 转经纬度地理坐标 弧度
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian3);
      // 弧度转经纬度
      const updatedPosition = Cesium.Cartesian3.fromDegrees(
        Cesium.Math.toDegrees(cartographic.longitude),
        Cesium.Math.toDegrees(cartographic.latitude),
        height
      );
      gltfEntity.position = updatedPosition;
    });

  Cesium.knockout.getObservable(viewModel, "rx").subscribe(function (rx) {
    rx = Number(rx);
    if (isNaN(rx)) return;
    // 获取当前位置
    const currentPosition = gltfEntity.position.getValue(
      viewer.clock.currentTime
    );
    // 使用新的 Pitch (绕 X 轴旋转) 更新实体的 orientation
    const newOrientation = Cesium.Transforms.headingPitchRollQuaternion(
      currentPosition,
      new Cesium.HeadingPitchRoll(0, Cesium.Math.toRadians(rx), 0) // 设置绕 X 轴的旋转角度
    );
    gltfEntity.orientation = newOrientation; // 更新 orientation
  });

  Cesium.knockout.getObservable(viewModel, "ry").subscribe(function (ry) {
    ry = Number(ry);
    if (isNaN(ry)) return;
    // 获取当前位置
    const currentPosition = gltfEntity.position.getValue(
      viewer.clock.currentTime
    );
    //更新实体的 orientation
    const newOrientation = Cesium.Transforms.headingPitchRollQuaternion(
      currentPosition,
      new Cesium.HeadingPitchRoll(0, 0, Cesium.Math.toRadians(ry)) // 设置绕 y 轴的旋转角度
    );
    gltfEntity.orientation = newOrientation; // 更新 orientation
  });

  Cesium.knockout.getObservable(viewModel, "rz").subscribe(function (rz) {
    rz = Number(rz);
    if (isNaN(rz)) return;
    // 获取当前位置
    const currentPosition = gltfEntity.position.getValue(
      viewer.clock.currentTime
    );
    //  更新实体的 orientation
    const newOrientation = Cesium.Transforms.headingPitchRollQuaternion(
      currentPosition,
      new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(rz), 0, 0) // 设置绕 z 轴的旋转角度
    );
    gltfEntity.orientation = newOrientation; // 更新 orientation
  });
}

/**
 * 加载gltf
 * @param viewer
 */
function loadGltfModel(viewer) {
  const position = Cesium.Cartesian3.fromDegrees(-120.05, 44, 0); // 经纬度转笛卡尔坐标
  const heading = Cesium.Math.toRadians(45); // 角度转弧度
  const pitch = 0;
  const roll = 0;
  const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll); // 偏转角
  const orientation = Cesium.Transforms.headingPitchRollQuaternion(
    position,
    hpr
  ); // 方位角
  const gltfEntity = viewer.entities.add({
    name: "gltf模型",
    position: position,
    orientation: orientation, // 模型方位，默认向东
    model: {
      show: true,
      uri: "/cesiumData/models/DracoCompressed/CesiumMilkTruck.gltf",
      scale: 1.0, // 缩放比例
      minimumPixelSize: 128, // 最小像素大小
      maximumScale: 20000, // 模型的最大比例尺大小。 minimumPixelSize的上限
      incrementallyLoadTextures: true, // 加载模型后纹理是否可以继续流入
      runAnimations: true, // 是否应启动模型中指定的glTF动画
      clampAnimations: true, // 指定glTF动画是否应在没有关键帧的持续时间内保持最后一个姿势
      // 指定模型是否投射或接收来自光源的阴影 type:ShadowMode
      // DISABLED	对象不投射或接收阴影;ENABLED 对象投射并接收阴影;CAST_ONLY	对象仅投射阴影;RECEIVE_ONLY	对象仅接收阴影
      shadows: Cesium.ShadowMode.ENABLED,
      heightReference: Cesium.HeightReference.NONE,
    },
  });
  return gltfEntity;
}
</script>

<style scoped lang="scss">
#cesiumContainer {
  width: 100%;
  height: 100%;
  position: relative;
  #toolbar {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 9;
    color: #fff;
    input {
      color: #000;
    }
  }
}
</style>
